﻿#pragma once

#include "../detail/box_alloc.hh"
#include "../util/box_std_allocator.hh"
#include "../util/time_util.hh"
#include "spsc_queue_pair.hpp"
#include <unordered_set>
#include <atomic>
#include <cstdint>
#include <ctime>
#include <functional>
#include <list>
#include <memory>
#include <thread>
#include <vector>

namespace kratos {
namespace service {
class ServiceBox;
}
} // namespace kratos

namespace kratos {
namespace util {

class TimerWheel;
struct TimerList;
struct WheelNode;

/**
 * 定时器ID.
 */
using TimerID = WheelNode *;
/**
 * 定时器回调.
 */
using Handler = std::function<bool(TimerID, std::uint64_t)>;

/**
 * 单级时间轮.
 */
class TimerWheel {
  using TimerSlot =
      std::vector<TimerList *, kratos::service::Allocator<TimerList *>>;
  TimerSlot timer_slot_;                           ///< 定时器槽位
  std::size_t worker_cur_slot_{0};                 ///< 当前检测槽位
  std::time_t last_tick_{0};                       ///< 上次检测的时间戳
  kratos::service::ServiceBox *box_{nullptr};      ///< 服务容器
  constexpr static std::time_t RESOLUTION = 10; ///< 定时器检测运行频率，毫秒
  constexpr static std::size_t SLOTS = 1000 / RESOLUTION; ///< 定时器槽位数量

public:
  /**
   * 构造.
   *
   * \param box 服务容器
   */
  TimerWheel(kratos::service::ServiceBox *box);
  /**
   * 析构.
   *
   */
  ~TimerWheel();
  /**
   * 主循环.
   *
   * \param now
   */
  void update(std::time_t now);
  /**
   * 启动一个定时器，每duration毫秒触发一次.
   *
   * \param handler 处理器
   * \param duration 延迟时间，毫秒
   * \param user_data 用户数据
   * \return 定时器ID
   */
  TimerID schedule(Handler handler, std::time_t duration,
                   std::uint64_t user_data);
  /**
   * 启动一个定时器，duration毫秒触发一次后自动销毁
   *
   * \param handler 处理器
   * \param duration 延迟时间，毫秒
   * \param user_data 用户数据
   * \return 定时器ID
   */
  TimerID schedule_once(Handler handler, std::time_t duration,
                        std::uint64_t user_data);
  /**
   * 取消并销毁定时器.
   *
   * \param id 定时器ID
   */
  void cancel(TimerID id);

private:
  /**
   * 循环.
   *
   * \param now 当前时间戳，毫秒
   */
  void worker_update(std::time_t now);
  /**
   * 检查当前槽内定时器.
   *
   * \param now 当前时间戳，毫秒
   */
  void worker_update_slot(std::time_t now);
  /**
   * 添加定时器.
   *
   * \param id 定时器ID
   * \param now 当前时间戳，毫秒
   */
  void worker_add(TimerID id, std::time_t now);
  /**
   * 销毁定时器
   *
   * \param id 定时器ID
   */
  void destroy(TimerID id);
};

extern std::size_t get_wheel_timer_count();

} // namespace util
} // namespace kratos
